
function Activity(){
    this.limitBuy = {};
    this.payRank = [];
    this.payRankTime = 0;
    this.gemBuy = {};
    this.dailyPayRank = {
        rank : {},
        time : 0,
    };
    this.richSend = {
        rank: [],     // 排行
        cash: 0,      // 总充值额
        day : 0,      // 日期
        time: 0,      // 活动开启时间
    };
    this.allGift = {
        rank: [],     // 排行 
        cash: 0,      // 总充值额
        day : 0,      // 日期
        time: 0,      // 活动开启时间
    };

    this.allCharge = {
        'time': 0,          // 活动开启时间
        'count' : {         // 全服达到不同充值金额的充值人数
            /*id:count*/    // 充值金额对应ID:大于等于该金额的充值人数 
        },
        'user' : {          // 已经充值玩家
            /*uid:id*/      // 玩家uid:充值对应等级
        },
    };

    this.yearFeedBack = {
        'time': 0,          // 活动开启时间
        'cash' : 0,         // 全服累计花费元宝
    };

    this.shotmearrow = {
        shotNum: 0, // 当日全服借箭数
        utime: 0,    // 上次更新时间
        ranks: []   // 排行
    };

    this.updates = {};
}

Activity.create = function() {
    var initActivity = {
        _id : 'activity',
        'limit_buy' : {     // 限量购买
        /*
            day : 20130811, // 日期
            sold : {}       // 已经出售数量
        */
        },
        'pay_rank' : [],    // 充值排行
        'pay_rank_time' : 0,// 充值排行时间 
        'gem_buy' : {       // 限量购买宝石
        /*
            day : 20130811, // 日期
            sold : {}       // 已经出售数量
        */
        },
        'daily_pay_rank' : {// 每日充值排行
            'rank': {},     // 每日排行
            'time': 0,      // 活动开启时间
        },

        'rich_send' : {     // 土豪送礼
            'rank': [],     // 排行
            'cash': 0,      // 总充值额
            'day' : 0,      // 日期
            'time': 0,      // 活动开启时间
        },

        'all_gift' : {      // 全民福利
            'rank': [],     // 排行
            'cash': 0,      // 总充值额
            'day' : 0,      // 日期
            'time': 0,      // 活动开启时间
        },

        'all_charge' : {    // 全民充值 
            'time': 0,      // 活动开启时间
            'count' : {     // 全服达到不同充值金额的充值人数
                /*id:count*/    
            },
            'user' : {          // 已经充值玩家
                /*uid:id*/      // 玩家uid:充值对应等级
            },
        },

        'year_feed_back' : {
            'time': 0,       // 活动开启时间
            'cash' : 0,      // 全服累计花费元宝
        },
        'shotmearrow' : {
            'shotNum': 0, // 当日全服借箭数
            'utime': 0,    // 上次更新时间
            'ranks': []   // 排行
        },
    };
    gDBWorld.insert(initActivity, function(err, result){}); 
};

Activity.prototype = {
    init : function(callback) {
        gDBWorld.findOne({_id : 'activity'}, {}, function(err, doc){
            if( doc ) {
                this.limitBuy = doc.limit_buy;
                if( doc.pay_rank ) {
                    this.payRank = doc.pay_rank;
                    this.payRankTime = doc.pay_rank_time;
                }

                if( doc.daily_pay_rank ) {
                    this.dailyPayRank = doc.daily_pay_rank;
                }

                if( doc.rich_send ) {
                    this.richSend = doc.rich_send;
                }

                if( doc.all_gift) {
                    this.allGift = doc.all_gift; 
                }

                if( doc.all_charge ) {
                    this.allCharge = doc.all_charge; 
                }

                if( doc.year_feed_back ) {
                    this.yearFeedBack = doc.year_feed_back; 
                }

                if( doc.shotmearrow) {
                    this.shotmearrow = doc.shotmearrow;
                }

                for (var key in gSpecActivitys) {
                    if (gSpecActivitys[key].world) {
                        this[key] = doc[key];
                    }
                }

                this.updatePayRankActivity();
                this.updateDailyPayRankActivity();
                this.updateRichSendActivity();
                this.updateAllGiftActivity();
                this.updateAllChargeActivity();
                this.updateYearFeedBackActivity();

                if( doc.gem_buy ) {
                    this.gemBuy = doc.gem_buy;
                }else{
                    this.addUpdate('gem_buy', this.gemBuy);
                }

                this.save();

                callback && callback(true);
            }else{
                callback && callback(false);
            }
        }.bind(this));
    },

    addUpdate : function(item, obj) {
        this.updates[item] = obj;
    },

    save : function(callback) {
        if( !this.updates || Object.keys(this.updates).length == 0 ) {
            callback && callback(true);
            return;
        }

        var updates = this.updates;
        gDBWorld.update({_id : 'activity'}, {$set:updates}, function(err, result){
            if( err ) {
                ERROR({updates:updates, err:err});
                callback && callback(false);
            }else {
                callback && callback(true);
            }
        });

        this.updates = {};
    },

    updateLimitBuy : function() {
        var today = common.getDate();
        if( this.limitBuy.day == today ) return this.limitBuy;

        this.limitBuy = {
            'day' : today,
            'sold' : {},
        };

        this.addUpdate('limit_buy', this.limitBuy);
        return this.limitBuy;
    },

    updatePayRankActivity : function() {
        var activitySchedule = findActivitySchedule('payrank');
        if( !activitySchedule ) return;

        if( activitySchedule.time == this.payRankTime ) {
            return;
        }

        this.payRank = [];
        this.payRankTime = activitySchedule.time;

        this.addUpdate('pay_rank', this.payRank);
        this.addUpdate('pay_rank_time', activitySchedule.time);
    },

    updatePayRank : function(uid, conf, paid){
        // 先更新活动周期
        this.updatePayRankActivity();
        
        var minPaid = 0;
        if( this.payRank.length > 0 ) {
            minPaid = this.payRank[this.payRank.length-1].paid;
        }

        if( this.payRank.length >= 10 && paid <= minPaid ) {
            return;
        }
        
        var userName = gUserInfo.getUser(uid).name;
        var inRank = false;
        for( var i=0; i<this.payRank.length; i++ ) {
            var rank = this.payRank[i];
            if( rank.uid == uid ) {
                rank.paid = paid;
                rank.name = userName;
                inRank = true;
                break;
            }
        }

        if( !inRank ) {
            this.payRank.push({'uid':uid, 'paid':paid, 'name':userName, 'rank':1, 'reward':0});
        }

        var payRank = this.payRank;
        payRank.sort(function(a, b){
            return b.paid - a.paid; 
        });

        var confAvPayRank = gConfAvPayRank[conf];
        var rankCursor = 0; // 遍历payRank列表游标
        for( var i=1; i<=5; i++ ) {
            var rankConf = confAvPayRank[i];
            var startRank = 1;
            if( i == 1) {
                startRank = rankConf.Rank;
            }else{
                startRank = confAvPayRank[i-1].Rank + 1;
            }
            var endRank = rankConf.Rank;
            var payMin = rankConf.PayMin;

            for( var j=startRank; j<=endRank; j++ ) {
                var rankData = payRank[rankCursor];
                if( !rankData ) break;

                if( rankData.paid >= payMin ) {
                    rankData.id = i;
                    rankData.rank = j;
                    rankCursor += 1;
                }
            }
        }

        var lastRank = Math.max(payRank[rankCursor-1].rank, confAvPayRank[5].Rank);
        for( var k=rankCursor; k<payRank.length; k++ ) {
            lastRank += 1;
            var rankData = payRank[k];
            rankData.rank = lastRank;
            delete rankData.id;
        }

        this.addUpdate('pay_rank', this.payRank);
    },

    updateRichSendActivity : function() {
        var activitySchedule = findActivitySchedule('richsend', 1);
        if( !activitySchedule ) return;
        
        var today = common.getDate();
        if( activitySchedule.time != this.richSend.time ) {
            this.richSend = {
                'rank': [],
                'cash': 0,
                'day' : today,
                'time': activitySchedule.time,
            };
            this.addUpdate('rich_send', this.richSend);
        }

        if( this.richSend.day != today ) {
            this.richSend.day = today;
            this.richSend.cash = 0;

            this.addUpdate('rich_send', this.richSend);
        }

    },

    updateAllGiftActivity : function() {
        var activitySchedule = findActivitySchedule('allgift');
        if( !activitySchedule ) return;
        
        var today = common.getDate();
        if( activitySchedule.time != this.allGift.time ) {
            this.allGift= {
                'rank': [],
                'cash': 0,
                'day' : today,
                'time': activitySchedule.time,
            };

            this.addUpdate('all_gift', this.allGift);
        }else if( this.allGift.day != today ) {
            this.allGift.rank = [];
            this.allGift.cash = 0;
            this.allGift.day = today;

            this.addUpdate('all_gift', this.allGift);
        }
    },

    updateGemBuy : function() {
        var today = common.getDate();
        if( this.gemBuy.day == today ) return this.gemBuy;

        this.gemBuy = {
            'day' : today,
            'sold' : {},
        };

        this.addUpdate('gem_buy', this.gemBuy);
        return this.gemBuy;
    },

    updateDailyPayRankActivity : function() {
        var activitySchedule = findActivitySchedule('dailypayrank');
        if( !activitySchedule ) return;

        if( activitySchedule.time == this.dailyPayRank.time ) {
            return;
        }

        this.dailyPayRank.rank = {};
        this.dailyPayRank.time = activitySchedule.time;

        this.addUpdate('daily_pay_rank', this.dailyPayRank);
    },

    updateDailyPayRank : function(uid, conf, day, paid) {
        this.updateDailyPayRankActivity();

        if( !this.dailyPayRank.rank[day] ) {
            this.dailyPayRank.rank[day] = [];
        }

        var payRank = this.dailyPayRank.rank[day];
        var avDayConf = gConfAvDailyPayRank[conf][day];

        var minPaid = 0;
        if( payRank.length > 0 ) {
            minPaid = payRank[payRank.length-1].paid;
        }

        if( payRank.length >= 10 && paid <= minPaid ) {
            return;
        }
        
        var userName = gUserInfo.getUser(uid).name;
        var inRank = false;
        for( var i=0; i<payRank.length; i++ ) {
            var rank = payRank[i];
            if( rank.uid == uid ) {
                rank.paid = paid;
                rank.name = userName;
                inRank = true;
                break;
            }
        }

        if( !inRank ) {
            payRank.push({'uid':uid, 'paid':paid, 'name':userName, 'rank':1});
        }

        payRank.sort(function(a, b){
            return b.paid - a.paid; 
        });

        var rankCursor = 0; // 遍历payRank列表游标
        for( var i=1; i<=5; i++ ) {
            var rankConf = avDayConf[i];
            var startRank = 1;
            if( i == 1) {
                startRank = rankConf.Rank;
            }else{
                startRank = avDayConf[i-1].Rank + 1;
            }
            var endRank = rankConf.Rank;
            var payMin = rankConf.PayMin;

            for( var j=startRank; j<=endRank; j++ ) {
                var rankData = payRank[rankCursor];
                if( !rankData ) break;

                if( rankData.paid >= payMin ) {
                    rankData.id = i;
                    rankData.rank = j;
                    rankCursor += 1;
                }
            }
        }

        var lastRank = Math.max(payRank[rankCursor-1].rank, avDayConf[5].Rank);
        for( var k=rankCursor; k<payRank.length; k++ ) {
            lastRank += 1;
            var rankData = payRank[k];
            rankData.rank = lastRank;
            delete rankData.id;
        }

        this.addUpdate('daily_pay_rank', this.dailyPayRank);
    },

    updateRichSend : function(uid, cash, paid) {
        // 先更新活动周期
        this.updateRichSendActivity();
        
        var now = common.getTime();
        if( now > gConfActivities.richsend.EndTime ) {
            return; 
        }

        this.richSend.cash += cash;
        if( !paid ) {
            this.addUpdate('rich_send', this.richSend);
            return;
        }
        
        var payRank = this.richSend.rank;

        var minPaid = 0;
        if( payRank.length > 0 ) {
            minPaid = payRank[payRank.length-1].paid;
        }

        if( payRank.length >= 10 && paid <= minPaid ) {
            this.addUpdate('rich_send', this.richSend);
            return;
        }
        
        var userName = gUserInfo.getUser(uid).name;
        var inRank = false;
        for( var i=0; i<payRank.length; i++ ) {
            var rank = payRank[i];
            if( rank.uid == uid ) {
                rank.paid = paid;
                rank.name = userName;
                inRank = true;
                break;
            }
        }

        if( !inRank ) {
            payRank.push({'uid':uid, 'paid':paid, 'name':userName, 'rank':1, 'reward':0});
        }

        payRank.sort(function(a, b){
            return b.paid - a.paid; 
        });

        var rankCursor = 0; // 遍历payRank列表游标
        for( var i=1; i<=5; i++ ) {
            var rankConf = gConfAvRichSendRank[i];
            var startRank = 1;
            if( i == 1) {
                startRank = rankConf.Rank;
            }else{
                startRank = gConfAvRichSendRank[i-1].Rank + 1;
            }
            var endRank = rankConf.Rank;
            var payMin = rankConf.PayMin;

            for( var j=startRank; j<=endRank; j++ ) {
                var rankData = payRank[rankCursor];
                if( !rankData ) break;

                if( rankData.paid >= payMin ) {
                    rankData.id = i;
                    rankData.rank = j;
                    rankCursor += 1;
                }
            }
        }

        var lastRank = Math.max(payRank[rankCursor-1].rank, gConfAvRichSendRank[5].Rank);
        for( var k=rankCursor; k<payRank.length; k++ ) {
            lastRank += 1;
            var rankData = payRank[k];
            rankData.rank = lastRank;
            delete rankData.id;
        }

        this.richSend.rank = payRank;

        this.addUpdate('rich_send', this.richSend);
    },

    updateAllGift : function( uid, cash, paid ) {
        // 先更新活动周期
        this.updateAllGiftActivity();

        var now = common.getTime();
        if( now > gConfActivities.allgift.EndTime ) return;

        this.allGift.cash += cash;
        if( !paid ) {
            this.addUpdate('all_gift', this.allGift); 
            return;
        }

        var payRank = this.allGift.rank;

        var minPaid = 0;
        if( payRank.length > 0 ) {
            minPaid = payRank[payRank.length - 1].paid; 
        }

        if( payRank.length >= 8 && paid <= minPaid ) {
            this.addUpdate('all_gift', this.allGift); 
            return;
        }

        var inRank = false;
        var userName = gUserInfo.getUser(uid).name;
        for( var i = 0,len = payRank.length; i < len; ++i ) {
            var rank = payRank[i];
            if( rank.uid == uid ) {
                rank.name = userName;
                rank.paid = paid;
                inRank = true;
            }
        }

        if( !inRank ) {
            payRank.push({ 'uid': uid, 'name': userName, 'paid': paid });
        }

        payRank.sort( function(a, b) {
           return b.paid - a.paid;  
        });

        if( payRank.length > 8 ) {
            payRank = payRank.slice( 0, 8 ); 
        }

        this.allGift.rank = payRank;

        this.addUpdate('all_gift', this.allGift);
    },

    updateAllChargeActivity : function() {
        var activitySchedule = findActivitySchedule('allcharge');
        if( !activitySchedule ) return;
        
        var today = common.getDate();
        if( activitySchedule.time != this.allCharge.time ) {
            this.allCharge= {
                'count' : {},
                'time': activitySchedule.time,
                'user' : {},
            };

            for( var id in gConfAvAllCharge ) {
                this.allCharge.count[id] = 0; 
            }

            this.addUpdate('all_charge', this.allCharge);
        }
    },

    updateAllCharge : function( uid, cash ) {
        // 先更新活动周期
        this.updateAllChargeActivity();

        var now = common.getTime();
        if( now > gConfActivities.allcharge.EndTime ) return;
        
        var isChange = false;
        var allChargeCount = this.allCharge.count;
        var allChargeUsers = this.allCharge.user;
        for( var id in gConfAvAllCharge ) {
            var targetCash = gConfAvAllCharge[id].CashMin; 
            if( cash < targetCash ) continue; 
            
            if( !(uid in allChargeUsers) ) {
                allChargeUsers[uid] = 0; 
            }

            if( allChargeUsers[uid] >= id ) continue; 

            allChargeUsers[uid] = +id;
            allChargeCount[id] += 1;
            isChange = true;
        }

        if( isChange ) {
            this.addUpdate('all_charge', this.allCharge);
        }
    },

    updateYearFeedBackActivity: function() {
        var activitySchedule = findActivitySchedule('yearfeedback');
        if( !activitySchedule ) return;
        
        var today = common.getDate();
        if( activitySchedule.time != this.yearFeedBack.time ) {
            this.yearFeedBack = {
                'cash' : 0,
                'time': activitySchedule.time,
            };

            this.addUpdate('year_feed_back', this.yearFeedBack);
        }
    },

    updateYearFeedBack : function( uid, cash ) {
        // 先更新活动周期
        this.updateYearFeedBackActivity();

        var now = common.getTime();
        if( now > gConfActivities.yearfeedback.EndTime ) return;
        
        this.yearFeedBack.cash += cash;
        this.addUpdate('year_feed_back', this.yearFeedBack);
    },

    updateGiveMeArrow: function(uid, shotNum, incV, un, time) {
        var thisActivity = this.getActivity('shotmearrow', time);
        var now = new Date();
        if (now.getDayPassed(thisActivity.utime) != 1) {
            // 数据更新不是今天的
            thisActivity.utime = now.getStamp();
            thisActivity.shotNum = 0;
        }
        if (shotNum != 0 && incV != 0) {
            // 更新时间
            thisActivity.utime = now.getStamp();
            // 更新全服借箭数
            thisActivity.shotNum += incV;
            if (shotNum >= +gConfGlobal.ShotMeArrowAtRankNum) {
                // 更新排名
                var rankSize = +gConfGlobal.ShotMeArrowRankSize;
                var ranks = thisActivity.ranks;
                if (ranks.length > 0) {
                    // 从排行榜移除玩家
                    for (var i=0; i<ranks.length; ++i) {
                        var rank = ranks[i];
                        if (rank.uid == uid) {
                            ranks.splice(i, 1);
                            break;
                        }
                    }

                    // 加入排行榜
                    var inRank = false;
                    for (var i=0; i<ranks.length; ++i) {
                        var rank = ranks[i];
                        if (shotNum > rank.shotNum) {
                            ranks.splice(i, 0, {
                                uid: uid,
                                shotNum: shotNum,
                                un: un
                            });
                            inRank = true;
                            break;
                        }
                    }

                    if (!inRank && ranks.length < rankSize) {
                        ranks.push({
                            uid: uid,
                            shotNum: shotNum,
                            un: un
                        });
                    }

                    if (ranks.length > rankSize) {
                        ranks = ranks.splice(0, rankSize);
                    }

                    thisActivity.ranks = ranks;
                } else {
                    thisActivity.ranks.push({
                        uid: uid,
                        shotNum: shotNum,
                        un: un
                    });
                }
            }
        }

        this.addUpdate('shotmearrow', thisActivity);
        this.save();
    },

    getActivity : function(key, time, args) {
        var activity = this[key];
        var changed = false;
        if (!activity || activity.time != time) {
            switch (key) {
                case 'godgift':         // 觉醒福利
                activity = {
                    server_count: 0,        // 全服觉醒数量
                }
                break;
                case 'luckycharge':     // 充值幸运转
                activity = {
                    ranks:[],               // 转动排行榜
                }
                break;
                case 'imperialexamination': // 御前科举
                activity = {
                    round: 0, 
                    ranks: [],
                }
                break;
                case 'shotmearrow': // 草船借箭
                    activity = {
                        shotNum: 0, // 当日全服借箭数
                        utime: 0,    // 上次更新时间
                        ranks: []   // 排行
                    };
                    break;
                default:
                activity = {};
            }

            activity.time = time;
            this[key] = activity;
            changed = true;
        }

        if (args) {
            switch (key) {
                case 'imperialexamination':
                    if (args.round != activity.round) {
                        activity.round = args.round;
                        activity.ranks = [];
                        changed = true; 
                    }
                    break;
                default:
                    break;
            }
        }

        if (changed) {
            this.addUpdate(key, activity);
            this.save();
        }

        return activity;
    },
};

exports.get_limit_buy = function(req, resp, onHandled) {
    var limitBuy = gActivity.updateLimitBuy();

    resp.data.sold = limitBuy.sold;

    gActivity.save();
    onHandled();
};

exports.do_limit_buy = function(req, resp, onHandled) {
    do {
        var id = req.args.id;
        var limitBuyConf = gConfAvLimitBuy[id];
        if( !limitBuyConf ) {
            resp.code = 1; resp.desc = 'invalid id'; break;
        }

        var limitBuy = gActivity.updateLimitBuy();
        var sold = limitBuy.sold[id] || 0;

        if( sold >= limitBuyConf.Limit ) {
            // 卖光了
            resp.data.over = 1; break;
        }

        sold += 1;
        limitBuy.sold[id] = sold;
        gActivity.addUpdate('limit_buy', limitBuy);

        resp.data.sold = sold;

        gActivity.save();

    }while(false);

    onHandled();
};

exports.update_payrank = function(req, resp, onHandled) {
    do {
        gActivity.updatePayRank(req.uid, req.args.conf, req.args.paid);

        gActivity.save();

    }while(false);

    onHandled();
};

exports.get_payrank_top = function(req, resp, onHandled) {

    gActivity.updatePayRankActivity();

    var payRank = gActivity.payRank;

    var maxRank = Math.min(10, payRank.length);
    var i= 0;
    for( ; i<maxRank ;i++ ) {
        if( payRank[i].rank >= 10 ) {
            break;
        }
    }
    
    var rank = 0;
    if (req.args.paid >= gConfAvPayRank[req.args.conf].CashMin) {
        for( var j=0; j<payRank.length; j++ ) {
            var rankData = payRank[j];
            if(rankData.uid == req.uid ) {
                rank = rankData.rank;
                break;
            }
        }
    }

    resp.data.pay_rank = payRank.slice(0, i+1);
    resp.data.rank = rank;
    onHandled();
};

exports.get_payrank_top10reward = function(req, resp, onHandled) {

    gActivity.updatePayRankActivity();

    var payRank = gActivity.payRank;

    var maxRank = Math.min(10, payRank.length);
    var rankData = null;
    for( var i=0; i<payRank.length ;i++ ) {
        var rank = payRank[i];
        if( !rank.id ) break;

        if( rank.uid == req.uid ) {
            rankData = rank;
            break;
        }
    }

    if( !rankData || !rankData.id ) {
        resp.code = 1; resp.desc = 'not in top10';
    }else{
        resp.data.rank = rankData.id;
    }
    onHandled();
};

exports.get_gem_buy = function(req, resp, onHandled) {
    var gemBuy = gActivity.updateGemBuy();

    resp.data.sold = gemBuy.sold;

    gActivity.save();
    onHandled();
};

exports.do_gem_buy = function(req, resp, onHandled) {
    do {
        var confId = req.args.conf_id;
        var id = req.args.id;
        var gemBuyConf = gConfAvGemBuy[confId][id];
        if( !gemBuyConf ) {
            resp.code = 1; resp.desc = 'invalid id'; break;
        }

        var gemBuy = gActivity.updateGemBuy();
        var sold = gemBuy.sold[id] || 0;

        if( gemBuyConf.Limit && sold >= gemBuyConf.Limit ) {
            // 卖光了
            resp.data.over = 1; break;
        }

        sold += 1;
        gemBuy.sold[id] = sold;
        gActivity.addUpdate('gem_buy', gemBuy);

        resp.data.sold = sold;

        gActivity.save();

    }while(false);

    onHandled();
};

exports.get_daily_payrank = function(req, resp, onHandled) {

    gActivity.updateDailyPayRankActivity();

    var payRank = gActivity.dailyPayRank.rank[req.args.day] || [];

    var maxRank = Math.min(10, payRank.length);
    var i= 0;
    for( ; i<maxRank ;i++ ) {
        if( payRank[i].rank >= 10 ) {
            break;
        }
    }
    
    var rank = 0;
    for( var j=0; j<payRank.length; j++ ) {
        var rankData = payRank[j];
        if(rankData.uid == req.uid && rankData.id) {
            rank = rankData.rank;
            break;
        }
    }

    resp.data.pay_rank = payRank.slice(0, i+1);
    resp.data.rank = rank;
    onHandled();
};

exports.get_daily_payrank_top10reward = function(req, resp, onHandled) {

    gActivity.updateDailyPayRankActivity();

    var day = req.args.day;
    var payRank = gActivity.dailyPayRank.rank[day] || [];

    var maxRank = Math.min(10, payRank.length);
    var rankData = null;
    for( var i=0; i<payRank.length ;i++ ) {
        var rank = payRank[i];
        if( !rank.id ) break;

        if( rank.uid == req.uid ) {
            rankData = rank;
            break;
        }
    }

    if( !rankData || !rankData.id ) {
        resp.code = 1; resp.desc = 'not in top10';
    }else if( rankData.reward ) {
        resp.code = 1; resp.desc = 'got';
    }else{
        resp.data.rank = rankData.id;
        rankData.reward = 1;
        gActivity.addUpdate('daily_pay_rank', gActivity.dailyPayRank);
        gActivity.save();
    }

    onHandled();
};

exports.update_daily_payrank = function(req, resp, onHandled) {
    do {
        gActivity.updateDailyPayRank(req.uid, req.args.conf, req.args.day, req.args.paid);

        gActivity.save();

    }while(false);

    onHandled();
};

exports.get_richsend = function(req, resp, onHandled) {
    do {
        gActivity.updateRichSendActivity();
        resp.data.cash = gActivity.richSend.cash;
    }while(false);

    onHandled();
};

exports.get_richsend_top10 = function(req, resp, onHandled) {
    gActivity.updateRichSendActivity();
    var payRank = gActivity.richSend.rank;

    var maxRank = Math.min(10, payRank.length);
    var i= 0;
    for( ; i<maxRank ;i++ ) {
        if( payRank[i].rank >= 10 ) {
            break;
        }
    }
    
    var rank = 0;
    for( var j=0; j<payRank.length; j++ ) {
        var rankData = payRank[j];
        if(rankData.uid == req.uid && rankData.id) {
            rank = rankData.rank;
            break;
        }
    }

    resp.data.pay_rank = payRank.slice(0, i+1);
    resp.data.rank = rank;
    onHandled();
};

exports.richsend_reward = function(req, resp, onHandled) {
    gActivity.updateRichSendActivity();
    if( gActivity.richSend.cash < req.args.cash ) {
        resp.code = 1; resp.desc = 'not reach';
    }
    onHandled();
};

exports.richsend_top10reward = function(req, resp, onHandled) {
    gActivity.updateRichSendActivity();
    var payRank = gActivity.richSend.rank;

    var maxRank = Math.min(10, payRank.length);
    var rankData = null;
    for( var i=0; i<payRank.length ;i++ ) {
        var rank = payRank[i];
        if( !rank.id ) break;

        if( rank.uid == req.uid ) {
            rankData = rank;
            break;
        }
    }

    if( !rankData || !rankData.id ) {
        resp.code = 1; resp.desc = 'not in top10';
    }else if( rankData.reward ) {
        resp.code = 1; resp.desc = 'got';
    }else{
        resp.data.rank = rankData.id;
        rankData.reward = 1;
        gActivity.addUpdate('rich_send', gActivity.richSend);
        gActivity.save();
    }

    onHandled();
};

exports.update_richsend = function(req, resp, onHandled) {
    do {
        gActivity.updateRichSend(req.uid, req.args.cash, req.args.paid);

        gActivity.save();

    }while(false);

    onHandled();
};

/**
 * 获取全服借箭数
 * @param req
 * @param resp
 * @param onHandled
 */
exports.get_shotmearrow = function(req, resp, onHandled) {
    gActivity.updateGiveMeArrow(req.uid, 0, 0, '', +req.args.time);
    resp.data.wShotNum = gActivity.shotmearrow.shotNum;
    onHandled && onHandled();
};

/**
 * 更新草船借箭
 * @param req
 * @param resp
 * @param onHandled
 */
exports.update_shotmearrow = function(req, resp, onHandled){
    gActivity.updateGiveMeArrow(req.uid, +req.args.shotNum,
        +req.args.incV, req.args.un, +req.args.time);
    resp.data.wShotNum = gActivity.shotmearrow.shotNum;
    onHandled && onHandled();
    gActivity.save();
};

/**
 * 获取排行榜
 * @param req
 * @param resp
 * @param onHandled
 */
exports.get_shotmearrow_ranks = function(req, resp, onHandled) {
    resp.data.ranks = gActivity.shotmearrow.ranks;
    onHandled && onHandled();
};

/**
 * 获取全服借箭数
 * @param req
 * @param resp
 * @param onHandled
 */
exports.get_shotmearrow_shotNum = function(req, resp, onHandled) {
    gActivity.updateGiveMeArrow(req.uid, 0, 0, '', +req.args.time);
    resp.data.shotNum = gActivity.shotmearrow.shotNum;
    onHandled && onHandled();
};

// 年兽BOSS
function YearBoss() { 

    this.yearBoss = {
        /*
            'blood':0,
            'kill':0,
            'kill_reward':0,
            'top': [{'uid':0, 'hurt':0}],
            'day':0, 
        */
    };

    this.hurts = {};
    this.updates = {}; 
    this.sortTime = {};
} 
 
YearBoss.create = function() { 
    gDBWorld.insert({_id: 'year_boss', year_boss: {}}, function(err, result){}); 
}; 
 
YearBoss.prototype = { 
     
    init: function(callback) { 
        gDBWorld.findOne({_id : 'year_boss'}, {}, function(err, doc){ 
            if( doc ) {
                this.yearBoss = doc.year_boss;
                this._init();
                callback && callback(true);
            }else{
                callback && callback(false);
            }    
        }.bind(this));
    },

    _init: function() {
        var yearBoss = this.yearBoss;
        if( !yearBoss.top ) return;
        var hurts = {};
        this.hurts = hurts;
        yearBoss.top.forEach(function(user){
            hurts[user.uid] = user.hurt;
        });
    },

    updateBoss: function() {
        // 更新yearBoss
        var yearBoss = this.yearBoss;
        var nowTime = common.getTime();
        var today = new Date();
        var todayStartTime = +(new Date(today.getFullYear(), today.getMonth(), today.getDate()))/1000;

        var startTime = todayStartTime + gConfGlobal.YearBossStartHour*3600 - 20; 
        var endTime = todayStartTime + gConfGlobal.YearBossEndHour*3600 + 20; 
        var nextResetTime = todayStartTime + gConfGlobal.YearBossResetHour*3600 + 86400;

        var todayDay = common.getDate();
        if( yearBoss.day == todayDay && startTime <= nowTime 
            &&  nowTime <= nextResetTime ) {
            return false;
        }

        // 重置yearBoss
        yearBoss = {
            'blood': this.getTodayBlood(),
            'kill':  0,
            'kill_reward': 0,
            'top': [],
            'day': common.getDate(), 
        };

        this.yearBoss = yearBoss;
        this.hurts = {};

        this.addUpdate('year_boss', yearBoss);
        
        return true;
    },

    getBoss: function() {
        return this.yearBoss;
    },

    addUpdate: function(key, value) {
        this.updates[key] = value;
    },

    save: function(callback) {
        if( Object.keys(this.updates).length == 0 ) {
            return;
        }    
             
        gDBWorld.update({_id : 'year_boss'}, {$set: this.updates}, function(err, result){ 
            if( err ) {
                callback && callback(false);
            }else {
                callback && callback(true);
            }    
        });  

        this.updates = {};   
    },

    getTodayBlood: function() {
        var today = new Date();
        var blood = gConfGlobal.YearBossHealthPerDay * Math.ceil((today/1000 - gConfGlobalServer.ServerStartTime)/86400);
        if( blood > gConfGlobal.YearBossMaxHealth ) {
            blood = gConfGlobal.YearBossMaxHealth;
        }

        return blood;
    },

    addHurt: function(uid, hurt) {
        var hurts = this.hurts;
        var newUser = false;
        if( uid in hurts ) {
            hurts[uid] += hurt;
        }else{
            newUser = true;
            hurts[uid] = hurt;
        }

        var nowTime = common.getTime();
        var sortTime = this.sortTime || 0;
        var yearBoss = this.yearBoss;

        var confYearBoss = gConfYearBoss[1];
        var confMonster = gConfMonster[confYearBoss.Boss];
        var bossBloodLeft =  yearBoss.blood / this.getTodayBlood();  // 百分比

        if( (nowTime - sortTime) >= 60 || yearBoss.kill || newUser || bossBloodLeft < 0.1 ) { 
            var top = [];
            for( var uid in hurts ) {
                var userInfo = gUserInfo.getUser(uid);
                top.push({'uid':uid, 'hurt':hurts[uid]});
            }

            top.sort(function(obj1, obj2){
                return obj2.hurt - obj1.hurt;
            });

            yearBoss.top = top;

            this.sortTime = nowTime; 
            this.addUpdate('year_boss.top', yearBoss.top);
        }
    },
}   

exports.get_year_boss = function(req, resp, onHandled) {
    do{
        var uid = req.uid;
        var confYearBoss = gConfYearBoss[1];
        gYearBoss.updateBoss();

        var yearBoss = gYearBoss.getBoss();
        var killReward = 0;
        var killName = '';
        var joinReward = 0;
        var rank5 = [];
        if( yearBoss.kill ) {
            var top = yearBoss.top.slice(0,5);
            top.forEach(function(obj) {
                var userInfo = gUserInfo.getUser(obj.uid);
                rank5.push({'name': userInfo.name, 'headpic': userInfo.headpic } ); 
            });

            // 参与奖
            var top = yearBoss.top;
            for( var i=1,len=top.length; i<=len; i++ ) {
                if( uid == top[i-1].uid ) {
                    break;
                }
            }

            var myRankType = 0;
            if( i == 1 ) {
                myRankType = 1;
            }else if( i <= 10 ) {
                myRankType = 2;
            }else if( i <= 50 ) {
                myRankType = 3;
            }else {
                myRankType = 4;
            }

            if( i == len+1 ) {
                joinReward = 0;
            }else {
                var gotRewardBoss = yearBoss.day;
                if( req.args.join == yearBoss.day 
                    && req.args.got_reward != gotRewardBoss ) {
                    joinReward = myRankType;
                } else if(req.args.join == yearBoss.day 
                    && req.args.got_reward == gotRewardBoss ) {
                    joinReward = -myRankType;
                } else {
                    joinReward = 0; 
                }
            }

            // 击杀奖
            if( yearBoss.kill ) {
                var userInfo = gUserInfo.getUser(yearBoss.kill);
                killName = userInfo.name; 
            }

            if( req.uid != yearBoss.kill ) {
                killReward = 0;
            }else {
                killReward = yearBoss.kill_reward;
            }
        }

        resp.data.bossid = yearBoss.day;
        resp.data.kill_reward = killReward;
        resp.data.kill_name = killName;
        resp.data.join_reward = joinReward;
        resp.data.rank5 = rank5;

        gYearBoss.save();

    }while(false);

    onHandled();
}

exports.rank_year_boss = function(req, resp, onHandled) {
    do {  
        var uid = req.uid;
        var selfHurt = 0;
        var yearBoss = gYearBoss.getBoss();
        var rankUserInfo = [];
        yearBoss.top.forEach(function(obj) {
            if( uid == obj.uid ) {
                selfHurt = obj.hurt;
            }

            var userInfo = gUserInfo.getUser(obj.uid);
            rankUserInfo.push({'name': userInfo.name});
        });

        resp.data.rank = yearBoss.top.slice(0, gConfGlobal.YearBossRankLimit);        
        resp.data.user_info = rankUserInfo;
        resp.data.self_hurt = selfHurt;

    }while(false) ;
    
    onHandled();
} 

exports.get_kill_reward_year_boss = function(req, resp, onHandled) {
    do {
        var yearBoss = gYearBoss.getBoss();
        if( yearBoss.kill != req.uid || yearBoss.kill_reward != 1 ) {
            resp.code = 1; resp.desc = 'no reward'; break;
        }

        yearBoss.kill_reward = -1;
        gYearBoss.addUpdate('year_boss.kill_reward', -1);

        gYearBoss.save();

    }while(false); 

    onHandled();
}

exports.get_reward_year_boss = function(req, resp, onHandled) {
    do {
        var yearBoss = gYearBoss.getBoss();

        if( !yearBoss.kill) {
            resp.code=1; resp.desc='no yearBoss or kill'; break;
        }

        var gotRewardBoss = yearBoss.day;
        if( req.args.got_reward == gotRewardBoss ) {
            resp.code= 1; resp.desc='got';break;
        }
        
        var rank = 0;
        var top = yearBoss.top;
        for( var i=1,len=top.length; i<=len; i++ ) {
            if( req.uid == top[i-1].uid ) {
                rank = i;
                break;
            }
        }

        if( rank <= 0 ) {
            resp.code=1; resp.desc='not join'; break;
        }
        
        var rewardRank = 1;
        if( rank == 1 ) {
            rewardRank = 1;
        }else if( rank <= 10 ) {
            rewardRank = 2;
        }else if( rank <= 50 ) {
            rewardRank = 10;
        }else {
            rewardRank = 50;
        }
        
        var yearBossRewardConf = gConfYearBossReward[rewardRank];
        var awards = [];
        if( yearBossRewardConf ) {
            awards = [yearBossRewardConf.Award1, yearBossRewardConf.Award2];
        }

        resp.data.bossid = gotRewardBoss;
        resp.data.awards = awards;

    }while(false);
    
    onHandled();
}

exports.fight_year_boss = function(req, resp, onHandled) {
    do {
        var uid = req.uid;
        var yearBoss = gYearBoss.getBoss();
        if( req.args.join != yearBoss.day ) {
            resp.code = 1; resp.desc = 'not ever update user data'; break;
        }

        if( yearBoss.kill ) {
            resp.data.killed = 1; break;
        }
        
        var oldBossBlood = yearBoss.blood;
        var yearBossConf = gConfYearBoss[1];
        fightYearBoss(uid, yearBossConf, oldBossBlood, function(battleReport) {
            if( !battleReport ) {
                resp.code = 1; resp.desc = 'fight yearBoss error';
                onHandled();
                return;
            }

            var bloodLeft = battleReport.def_remain_health;
            var hurt = oldBossBlood - bloodLeft;
            yearBoss.blood = bloodLeft;
            gYearBoss.addUpdate('year_boss.blood', bloodLeft);

            gYearBoss.addHurt(uid, hurt);

            var success = battleReport.success;
            if( success ) {
                yearBoss.kill = uid;
                gYearBoss.addUpdate('year_boss.kill', uid ); 

                yearBoss.kill_reward = 1;
                gYearBoss.addUpdate('year_boss.kill_reward', 1 );

                gMessage.addSystemMessage('year_boss_kill', [gUserInfo.getUser(uid).name]);
            }   

            resp.data.battle = battleReport;
            resp.data.time = common.getTime();

            gYearBoss.save();  
            onHandled();
        });

        return;
    }while(false);

    onHandled();
}

exports.boss_blood_year_boss = function(req, resp, onHandled) {
    do{
        var yearBoss = gYearBoss.getBoss();
        if( req.args.join != yearBoss.day ) {
            resp.code = 1; resp.desc = 'not ever update user data'; break;
        }

        resp.data.blood = yearBoss.blood; 
        resp.data.killed = yearBoss.blood>0 ? 0 : 1; 

    }while(false);

    onHandled();
}


exports.get_all_gift = function(req, resp, onHandled) {
    do {
        gActivity.updateAllGiftActivity(); 

        resp.data.paid_all = gActivity.allGift.cash;
        resp.data.rank = gActivity.allGift.rank;

    }while( false );

    onHandled();
}

exports.get_all_gift_reward = function(req, resp, onHandled) {
    do {
        gActivity.updateAllGiftActivity(); 

        if( !req.args.cash || gActivity.allGift.cash < req.args.cash ) {
            resp.code = 1; resp.desc = 'not reach all gift'; break;
        }

    }while( false );

    onHandled();
}

exports.get_all_charge = function(req, resp, onHandled) {
    do {
        gActivity.updateAllChargeActivity(); 

        resp.data.charge_count = gActivity.allCharge.count;

    }while( false );

    onHandled();
}

exports.all_charge_reward = function(req, resp, onHandled) {
    do {
        gActivity.updateAllChargeActivity(); 
        var id = req.args.id;
        var avAllChargeConf = gConfAvAllCharge[id];
        if( !avAllChargeConf ) {
            resp.code = 1; resp.desc = 'id error'; break;
        }

        var allChargeCount = gActivity.allCharge.count;
        if( !allChargeCount[id] || +allChargeCount[id] < avAllChargeConf.ChargeCount ) {
            resp.code = 1; resp.desc = 'not reach'; break;
        }
    }while( false );

    onHandled();
}

exports.get_year_feed_back = function(req, resp, onHandled) {
    do {
        gActivity.updateYearFeedBackActivity(); 

        resp.data.cost_cash = gActivity.yearFeedBack.cash;

    }while( false );

    onHandled();
}

exports.year_feed_back_reward = function(req, resp, onHandled) {
    do {
        gActivity.updateYearFeedBackActivity(); 
        var id = req.args.id;
        var avYearFeedBackConf = gConfAvYearFeedBack[id];
        if( !avYearFeedBackConf ) {
            resp.code = 1; resp.desc = 'id error'; break;
        }

        var yearFeedBackCash = gActivity.yearFeedBack.cash;
        if( yearFeedBackCash < avYearFeedBackConf.CostCashMin ) {
            resp.code = 1; resp.desc = 'not reach'; break;
        }

    }while( false );

    onHandled();
}

exports.update_all_gift = function(req, resp, onHandled) {
    do {
        gActivity.updateAllGift( req.uid, req.args.cash, req.args.paid );

        gActivity.save();

    }while( false );    

    onHandled();
}

exports.update_all_charge = function(req, resp, onHandled) {
    do {
        gActivity.updateAllCharge( req.uid, req.args.cash );

        gActivity.save();

    }while( false );    

    onHandled();
}

exports.update_year_feed_back = function(req, resp, onHandled) {
    do {
        gActivity.updateYearFeedBack( req.uid, req.args.cash );

        gActivity.save();

    }while( false );    

    onHandled();
}

exports.get_standard_activity_info = function(req, resp, onHandled) {
    do {
        var key = req.args.key;
        var time = req.args.time;

        resp.data[key] = gActivity.getActivity(key, time, req.args);
    } while (false);
    
    onHandled && onHandled();
}

exports.add_activity_value = function(req, resp, onHandled) {
    do {
        var key = req.args.key;
        var time = req.args.time;
        var attr = req.args.attr;
        var value = +req.args.value;

        var activity = gActivity.getActivity(key, time);
        if (!activity) break;

        addAttrValue(activity, attr, value);
        gActivity.addUpdate(key, activity);
        gActivity.save();

        resp.data[key] = activity;
    } while(false);

    onHandled && onHandled();
}

exports.luckycharge_update_rank = function(req, resp, onHandled) {
    do {
        var uid = req.uid;
        var confId = req.args.conf;
        var times = req.args.times;
        var activityKey = 'luckycharge';

        var activity = gActivity.getActivity(activityKey, req.args.time);
        if (!activity) {
            resp.code = 1; resp.desc = "activity invalid"; break;
        }

        var ranks = activity.ranks;
        var rankCount = ranks.length;
        var minTimes = 0;

        if (rankCount > 0) {
            minTimes = ranks[rankCount-1].times;
        }

        var rankMax = gConfRuntime.LuckyChargeRankMax[confId];
        if (rankCount >= rankMax && times <= minTimes) {
            resp.code = 1; resp.desc = "times not enough"; break;
        }

        var inRank = false;
        for (var i = 0; i < rankCount; ++i) {
            var rank = ranks[i];
            if (uid == rank.uid) {
                rank.times = times;
                inRank = true;
                break;
            }
        }

        if (!inRank) {
            ranks.push({'uid':uid, 'times':times, 'rank':1, 'id':1, 'award':0});
        }

        ranks.sort(function(a, b){
            return b.times - a.times; 
        });

        var avRankConf = gConfAvLuckyChargeRank[confId];
        var rankCursor = 0; // 遍历payRank列表游标
        var startRank = 1;
        for (var id in avRankConf) {
            var rankConf = avRankConf[id];
            var endRank = rankConf.Rank;
            var throwMin = rankConf.ThrowMin;

            for (var rank = startRank; rank <= endRank; ++rank) {
                var rankData = ranks[rankCursor];
                if (!rankData) break;

                if (rankData.times >= throwMin) {
                    rankData.id = id;
                    rankData.rank = rank;
                    rankCursor += 1;
                }
            }

            startRank = endRank + 1;
        }

        for (var i = rankCursor; i < ranks.length; ++i) {
            delete ranks[i].id;
        }

        activity.ranks = ranks;
        gActivity.addUpdate(activityKey, activity);
        gActivity.save();
    } while (false);

    onHandled && onHandled();
};

exports.luckycharge_get_rank = function(req, resp, onHandled) {
    do {
        var uid = req.uid;
        var activityKey = 'luckycharge';

        var activity = gActivity.getActivity(activityKey, req.args.time);
        if (!activity) {
            resp.code = 1; resp.desc = "activity invalid"; break;
        }

        var ranks = activity.ranks;
        var rankCount = ranks.length;
        
        var respRanks = [];
        var selfRank = 0;
        for (var i = 0; i < rankCount; ++i) {
            var rankData = ranks[i];
            if (!rankData.id) {
                continue;
            }

            var userInfo = gUserInfo.getUser(rankData.uid);
            if (userInfo) {
                respRankData = clone(rankData);
                respRankData.name = userInfo.name;
                respRanks.push(respRankData);
            }

            if (uid == rankData.uid) {
                selfRank = rankData.rank;
            }
        }

        resp.data.ranks = respRanks;
        resp.data.rank = selfRank;
    
    } while (false);

    onHandled && onHandled();
}

exports.luckycharge_get_rank_award = function(req, resp, onHandled) {
    do {
        var uid = req.uid;
        var activityKey = 'luckycharge';

        var activity = gActivity.getActivity(activityKey, req.args.time);
        if (!activity) {
            resp.code = 1; resp.desc = "activity invalid"; break;
        }

        var ranks = activity.ranks;
        var rankCount = ranks.length;

        var rankData = null;
        for (var i = 0; i < rankCount; ++i) {
            if (uid == ranks[i].uid) {
                rankData = ranks[i];
                break;
            }
        }

        if (!rankData || !rankData.id) {
            resp.code = 1; resp.desc = "not in rank"; break;
        }

        if (rankData.award) {
            resp.code = 1; resp.desc = "got"; break;
        }

        rankData.award = 1;
        gActivity.addUpdate(activityKey, activity);
        gActivity.save();

        resp.data.id = rankData.id;
    } while (false);

    onHandled && onHandled();
}

exports.imperialexamination_update_rank = function(req, resp, onHandled) {
    do {
        var uid = req.uid;
        var key = 'imperialexamination';

        var activity = gActivity.getActivity(key, req.args.time, req.args);
        if (!activity) {
            resp.code = 1; resp.desc = "activity invalid"; break;
        }

        if (activity.ranks.length < 3) {
            var userInfo = gUserInfo.getUser(uid);
            if (userInfo) {
                activity.ranks.push({uid:uid, name:userInfo.name, stamp:Date.getStamp(), award:0});
                gActivity.addUpdate(key, activity);
                gActivity.save();
            }
        }
    } while (false);

    onHandled && onHandled();
}

exports.imperialexamination_rank_award = function(req, resp, onHandled) {
    do {
        var uid = req.uid;
        var key = 'imperialexamination';

        var activity = gActivity.getActivity(key, req.args.time);
        if (!activity) {
            resp.code = 1; resp.desc = "activity invalid"; break;
        }

        var ranks = activity.ranks;
        var rankCount = ranks.length;

        var rank = 0;
        var rankData = null;
        for (var i = 0; i < rankCount; ++i) {
            if (uid == ranks[i].uid) {
                rankData = ranks[i];
                rank = i + 1;
                break;
            }
        }

        if (!rankData) {
            resp.code = 1; resp.desc = "not in rank"; break;
        }

        if (rankData.award) {
            resp.code = 1; resp.desc = "got"; break;
        }

        rankData.award = 1;
        gActivity.addUpdate(key, activity);
        gActivity.save();
        
        resp.data.rank = rank;
    } while (false);

    onHandled && onHandled();
}

exports.Activity = Activity;
exports.YearBoss = YearBoss;
